project(PANDA_PROPJECTS)

option(DISABLE_PANDA_PROJECTS "Disable build and run panda targets. Case when you need only cpp part" OFF)

if (DISABLE_PANDA_PROJECTS)
    message(INFO "Panda projects is disabled")
    return()
endif()

option(USE_LOCAL_BUILD_PANDA "Use local panda build instead of koala" OFF)
option(BUILD_PANDA_PATH "Path to local panda build" "/home/aleks/build_dirs/panda_sdks_buids/sdk_host_relwithdebinfo/linux_host_tools")
option(NPM_KOALA_PANDA_PATH "Path to dir with panda sdk" "/home/aleks/repos/idlize/external/incremental/tools/panda/node_modules/@panda/sdk/linux_host_tools")

if (USE_LOCAL_BUILD_PANDA AND NOT EXISTS "${BUILD_PANDA_PATH}")
    message(FATAL_ERROR "BUILD_PANDA_PATH does NOT exist: ${BUILD_PANDA_PATH}")
elseif(NOT USE_LOCAL_BUILD_PANDA AND NOT EXISTS "${NPM_KOALA_PANDA_PATH}")
    message(FATAL_ERROR "NPM_KOALA_PANDA_PATH does NOT exist: ${NPM_KOALA_PANDA_PATH}")
endif()

if (USE_LOCAL_BUILD_PANDA)
    set(SRC_PANDA_PATH "/home/aleks/repos/panda/static_core")
    set(BUILD_ETSSTDLIB_ABC "${BUILD_PANDA_PATH}/plugins/ets/etsstdlib.abc")
    set(BUILD_ETSSTDLIB_SRC_DIR "${SRC_PANDA_PATH}/plugins/ets/stdlib")
    set(BUILD_ANI_INCLUDE_DIR "${SRC_PANDA_PATH}/plugins/ets/runtime/ani")
    set(BUILD_ANI_HELPERS_INCLUDE_DIR "${SRC_PANDA_PATH}/plugins/ets/runtime/libani_helpers")
    set(BUILD_LIBARKRUNTIME_LIB_PATH "${BUILD_PANDA_PATH}/lib/libarkruntime.so")
    set(BUILD_LIBANI_HELPERS_LIB_PATH "${BUILD_PANDA_PATH}/lib/libani_helpers.so")

    set(DEFAULT_PANDA_PATH "${BUILD_PANDA_PATH}")
    set(DEFAULT_ETSSTDLIB_ABC "${BUILD_ETSSTDLIB_ABC}")
    set(DEFAULT_ETSSTDLIB_SRC_DIR "${BUILD_ETSSTDLIB_SRC_DIR}")
    set(DEFAULT_ANI_HELPERS_INCLUDE_DIR "${BUILD_ANI_HELPERS_INCLUDE_DIR}")
    set(DEFAULT_ANI_INCLUDE_DIR "${BUILD_ANI_INCLUDE_DIR}")
    set(DEFAULT_LIBARKRUNTIME_LIB_PATH "${BUILD_LIBARKRUNTIME_LIB_PATH}")
    set(DEFAULT_LIBANI_HELPERS_LIB_PATH "${BUILD_LIBANI_HELPERS_LIB_PATH}")
else()
    set(NPM_ETSSTDLIB_ABC "${NPM_KOALA_PANDA_PATH}/../ets/etsstdlib.abc")
    set(NPM_ETSSTDLIB_SRC_DIR "${NPM_KOALA_PANDA_PATH}/../ets/stdlib")
    set(NPM_ANI_INCLUDE_DIR "${NPM_KOALA_PANDA_PATH}/../ohos_arm64/include/plugins/ets/runtime/ani")
    set(NPM_ANI_HELPERS_INCLUDE_DIR "${NPM_KOALA_PANDA_PATH}/../ohos_arm64/include/plugins/ets/runtime/libani_helpers")

    if ("${CMAKE_CXX_COMPILER_TARGET}" STREQUAL "aarch64-linux-ohos")
        set(NPM_LIBARKRUNTIME_LIB_PATH "${NPM_KOALA_PANDA_PATH}/../ohos_arm64/lib/libarkruntime.so")
        set(NPM_LIBANI_HELPERS_LIB_PATH "${NPM_KOALA_PANDA_PATH}/../ohos_arm64/lib/libani_helpers.so")
    else()
        set(NPM_LIBARKRUNTIME_LIB_PATH "${NPM_KOALA_PANDA_PATH}/lib/libarkruntime.so")
        set(NPM_LIBANI_HELPERS_LIB_PATH "${NPM_KOALA_PANDA_PATH}/lib/libani_helpers.so")
    endif()

    set(DEFAULT_PANDA_PATH ${NPM_KOALA_PANDA_PATH})
    set(DEFAULT_ETSSTDLIB_ABC "${NPM_ETSSTDLIB_ABC}")
    set(DEFAULT_ETSSTDLIB_SRC_DIR "${NPM_ETSSTDLIB_SRC_DIR}")
    set(DEFAULT_ANI_HELPERS_INCLUDE_DIR "${NPM_ANI_HELPERS_INCLUDE_DIR}")
    set(DEFAULT_ANI_INCLUDE_DIR "${NPM_ANI_INCLUDE_DIR}")
    set(DEFAULT_LIBARKRUNTIME_LIB_PATH "${NPM_LIBARKRUNTIME_LIB_PATH}")
    set(DEFAULT_LIBANI_HELPERS_LIB_PATH "${NPM_LIBANI_HELPERS_LIB_PATH}")
endif()

set(ES2PANDA "${DEFAULT_PANDA_PATH}/bin/es2panda")
set(PANDA "${DEFAULT_PANDA_PATH}/bin/ark")
set(PANDA_AOT "${DEFAULT_PANDA_PATH}/bin/ark_aot")
set(PANDA_DISASM "${DEFAULT_PANDA_PATH}/bin/ark_disasm")
set(PANDA_LINK "${DEFAULT_PANDA_PATH}/bin/ark_link")
set(ETSSTDLIB_ABC "${DEFAULT_ETSSTDLIB_ABC}")
set(ETSSTDLIB_SRC_DIR "${DEFAULT_ETSSTDLIB_SRC_DIR}")
set(ANI_INCLUDE_DIR "${DEFAULT_ANI_INCLUDE_DIR}")
set(ANI_HELPERS_INCLUDE_DIR "${DEFAULT_ANI_HELPERS_INCLUDE_DIR}")
set(LIBARKRUNTIME_LIB_PATH "${DEFAULT_LIBARKRUNTIME_LIB_PATH}")
set(LIBANI_HELPERS_LIB_PATH "${DEFAULT_LIBANI_HELPERS_LIB_PATH}")

add_library(arkruntime SHARED IMPORTED)
set_property(TARGET arkruntime PROPERTY IMPORTED_LOCATION ${LIBARKRUNTIME_LIB_PATH})

add_library(libani_helpers SHARED IMPORTED)
set_property(TARGET libani_helpers PROPERTY IMPORTED_LOCATION ${LIBANI_HELPERS_LIB_PATH})

function(compile_panda_source)
    set(ONEVALUEARGS ARKTSCONFIG SRC_FILE_NAME TARGET_NAME OUTPUT_FILE_NAME)
    set(MULTIVALUEARGS EXTRA_DEPS)
    cmake_parse_arguments(PARSE_ARGV 0 ARG
        "" "${ONEVALUEARGS}" "${MULTIVALUEARGS}"
    )

    if (NOT DEFINED ARG_SRC_FILE_NAME OR
        NOT DEFINED ARG_TARGET_NAME )
        message(FATAL_ERROR "missing mandatory arguments")
    endif()

    if (NOT DEFINED ARG_ARKTSCONFIG OR ARG_ARKTSCONFIG STREQUAL "")
        set(ARG_ARKTSCONFIG "${CMAKE_CURRENT_BINARY_DIR}/default_arktsconfig.json")
        configure_file("${DEFAULT_ARKTSCONFIG_JSON_IN}" "${ARG_ARKTSCONFIG}")
    endif()

    cmake_path(ABSOLUTE_PATH ARG_SRC_FILE_NAME BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

    if (NOT DEFINED ARG_OUTPUT_FILE_NAME)
        cmake_path(GET ARG_SRC_FILE_NAME STEM ARG_OUTPUT_FILE_NAME)
        set(ARG_OUTPUT_FILE_NAME "${ARG_OUTPUT_FILE_NAME}.abc")
    endif()

    cmake_path(ABSOLUTE_PATH ARG_OUTPUT_FILE_NAME
        BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
    )

    add_custom_target(${ARG_TARGET_NAME}
        COMMENT "Compiling: ${ARG_SRC_FILE_NAME}"
        COMMAND ${ES2PANDA}
            --arktsconfig=${ARG_ARKTSCONFIG}
            ${ARG_SRC_FILE_NAME}
            --output=${ARG_OUTPUT_FILE_NAME}
        COMMAND ${PANDA_DISASM} ${ARG_OUTPUT_FILE_NAME} ${ARG_OUTPUT_FILE_NAME}.pa
        DEPENDS "${ARG_SRC_FILE_NAME}" ${ARG_EXTRA_DEPS}
        VERBATIM
    )
endfunction()

function(ark_link)
    set(ONEVALUEARGS TARGET_NAME OUTPUT_FILE)
    set(MULTIVALUEARGS ABC_FILE_LIST EXTRA_DEPS)
    cmake_parse_arguments(PARSE_ARGV 0 ARG
        "" "${ONEVALUEARGS}" "${MULTIVALUEARGS}"
    )

    if (NOT DEFINED ARG_OUTPUT_FILE OR NOT DEFINED ARG_ABC_FILE_LIST)
        message(FATAL_ERROR "missing mandatory arguments")
    endif()


    add_custom_target(${ARG_TARGET_NAME}
        COMMENT "Run ark_link: ${ARG_ABC_FILE_LIST}"
        COMMAND ${PANDA_LINK}
            --output=${ARG_OUTPUT_FILE}
            --
            ${ARG_ABC_FILE_LIST}
        COMMAND ${PANDA_DISASM} ${ARG_OUTPUT_FILE} ${ARG_OUTPUT_FILE}.pa
        DEPENDS ${ARG_EXTRA_DEPS}
        VERBATIM
    )
endfunction()

function(abc2an_name ABC_FILE_PATH RESULT_AN_FILE_PATH)
    string(REPLACE ".abc" ".an" AN_FILE_PATH "${ABC_FILE_PATH}")
    set(${RESULT_AN_FILE_PATH} "${AN_FILE_PATH}" PARENT_SCOPE)
endfunction()

function(ark_aot)
    set(ONEVALUEARGS TARGET_NAME ABC_FILE OUTPUT_FILE)
    set(MULTIVALUEARGS EXTRA_DEPS EXTRA_BOOT_PANDA_FILES)
    cmake_parse_arguments(PARSE_ARGV 0 ARG
        "" "${ONEVALUEARGS}" "${MULTIVALUEARGS}"
    )

    if (NOT DEFINED ARG_ABC_FILE)
        message(FATAL_ERROR "missing mandatory arguments")
    endif()

    set(BOOT_PANDA_FILES_OPT "--boot-panda-files=${ETSSTDLIB_ABC}")
    if (DEFINED ARG_EXTRA_BOOT_PANDA_FILES)
        set(BOOT_PANDA_FILES_OPT "${BOOT_PANDA_FILES_OPT}:${ARG_EXTRA_BOOT_PANDA_FILES}")
        string(REPLACE ";" ":" BOOT_PANDA_FILES_OPT "${BOOT_PANDA_FILES_OPT}")
    endif()

    if (NOT DEFINED ARG_OUTPUT_FILE)
        abc2an_name("${ARG_ABC_FILE}" ARG_OUTPUT_FILE)
    endif()

    add_custom_target(${ARG_TARGET_NAME}
        COMMENT "Run ark_aot: ${ARG_ABC_FILE}"
        COMMAND ${PANDA_AOT}
            --paoc-panda-files=${ARG_ABC_FILE}
            --paoc-output=${ARG_OUTPUT_FILE}
            --load-runtimes=ets
            ${BOOT_PANDA_FILES_OPT}
        DEPENDS ${ARG_EXTRA_DEPS}
        VERBATIM
    )
endfunction()

function(compile_panda_source_list)
    set(ONEVALUEARGS ARKTSCONFIG TARGET_NAME ARK_LINK_OUTPUT_FILE PREFIX_OUTPUT_FILE_NAME)
    set(MULTIVALUEARGS SOURCE_LIST EXTRA_DEPS)
    cmake_parse_arguments(PARSE_ARGV 0 ARG
        "" "${ONEVALUEARGS}" "${MULTIVALUEARGS}"
    )

    if (NOT DEFINED ARG_SOURCE_LIST OR
        NOT DEFINED ARG_TARGET_NAME)
        message(FATAL_ERROR "missing mandatory arguments")
    endif()

    set(SUBTARGETS "${ARG_TARGET_NAME}_build_subtargets")
    add_custom_target("${SUBTARGETS}")

    add_custom_target("${ARG_TARGET_NAME}"
        DEPENDS
            ${ARG_EXTRA_DEPS}
            ${SUBTARGETS}
    )

    set(ABC_FILE_LIST "")
    foreach (SRC ${ARG_SOURCE_LIST})
        cmake_path(ABSOLUTE_PATH SRC BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} NORMALIZE)
        string(REPLACE "." "_" SUBTARGET "${SRC}")
        string(REPLACE "/" "_" SUBTARGET "${SRC}")
        set(SUBTARGET "${SUBTARGET}_${ARG_TARGET_NAME}")
        cmake_path(GET SRC STEM ABC_FULL_PATH)

        if (DEFINED ARG_PREFIX_OUTPUT_FILE_NAME)
            set(ABC_FULL_PATH "${ABC_FULL_PATH}_${ARG_PREFIX_OUTPUT_FILE_NAME}")
        endif()

        set(ABC_FULL_PATH "${ABC_FULL_PATH}.abc")

        list(APPEND ABC_FILE_LIST "${ABC_FULL_PATH}")
        compile_panda_source(
            TARGET_NAME "${SUBTARGET}"
            SRC_FILE_NAME ${SRC}
            ARKTSCONFIG "${ARG_ARKTSCONFIG}"
            OUTPUT_FILE_NAME "${ABC_FULL_PATH}"
        )

        add_dependencies("${SUBTARGETS}" "${SUBTARGET}")
    endforeach()

    if (DEFINED ARG_ARK_LINK_OUTPUT_FILE)
        set(ARK_LINK_TARGET "${ARG_TARGET_NAME}_ark_link")
        ark_link(TARGET_NAME ${ARK_LINK_TARGET}
            ABC_FILE_LIST ${ABC_FILE_LIST}
            OUTPUT_FILE "${ARG_ARK_LINK_OUTPUT_FILE}"
            EXTRA_DEPS "${SUBTARGETS}"
        )
        add_dependencies("${ARG_TARGET_NAME}" "${ARK_LINK_TARGET}")
    endif()

endfunction()

function(run_panda_abc)
    set(ONEVALUEARGS ABC_FILE_NAME TARGET_NAME)
    set(MULTIVALUEARGS ADDITIONAL_OPTIONS EXTRA_DEPS EXTRA_BOOT_PANDA_FILES)
    cmake_parse_arguments(PARSE_ARGV 0 ARG
        "" "${ONEVALUEARGS}" "${MULTIVALUEARGS}"
    )

    if (NOT DEFINED ARG_ABC_FILE_NAME OR NOT DEFINED ARG_TARGET_NAME)
        message(FATAL_ERROR "missing mandatory arguments")
    endif()

    set(ABC_FULL_PATH "${CMAKE_CURRENT_BINARY_DIR}/${ARG_ABC_FILE_NAME}")
    set(BOOT_PANDA_FILES_OPT "--boot-panda-files=${ETSSTDLIB_ABC}")

    if (DEFINED ARG_EXTRA_BOOT_PANDA_FILES AND NOT ARG_ARKTSCONFIG STREQUAL "")
        set(BOOT_PANDA_FILES_OPT "${BOOT_PANDA_FILES_OPT}:${ARG_EXTRA_BOOT_PANDA_FILES}")
        string(REPLACE ";" ":" BOOT_PANDA_FILES_OPT "${BOOT_PANDA_FILES_OPT}")
    endif()

    get_filename_component(START_PACKAGE_NAME "${ARG_ABC_FILE_NAME}" NAME_WE)
    add_custom_target(${ARG_TARGET_NAME} ALL
        COMMENT "Run abc-file: ${ABC_FULL_PATH}"
        COMMAND
            taskset -c 1
            ${PANDA}
            --load-runtimes=ets
            ${BOOT_PANDA_FILES_OPT}
            ${ARG_ADDITIONAL_OPTIONS}
            ${ABC_FULL_PATH}
            "${START_PACKAGE_NAME}.ETSGLOBAL::main"
        DEPENDS ${ARG_EXTRA_DEPS}
        VERBATIM
    )
endfunction()

function(run_panda)
    set(OPTIONSEARGS AOT)
    set(ONEVALUEARGS TARGET_NAME ARKTSCONFIG SRC_FILE_NAME)
    set(MULTIVALUEARGS ADDITIONAL_OPTIONS EXTRA_DEPS EXTRA_BOOT_PANDA_FILES MAKE_EXTRA_AOT_FILES)
    cmake_parse_arguments(PARSE_ARGV 0 ARG
        "${OPTIONSEARGS}" "${ONEVALUEARGS}" "${MULTIVALUEARGS}"
    )

    set(TARGET_NAME_COMPILE_ABC "${ARG_TARGET_NAME}_compile_abc")

    compile_panda_source(
        SRC_FILE_NAME "${ARG_SRC_FILE_NAME}"
        ARKTSCONFIG "${ARG_ARKTSCONFIG}"
        TARGET_NAME ${TARGET_NAME_COMPILE_ABC}
    )

    string(REPLACE ".ets" ".abc" GEN_ABC_FILE_NAME "${ARG_SRC_FILE_NAME}")


    unset(AOT_FILES_OPTION)

    if (ARG_AOT)
        set(TARGET_NAME_ARK_AOT "${ARG_TARGET_NAME}_aot")
        abc2an_name("${GEN_ABC_FILE_NAME}" GEN_AOT_FILE_NAME)
        ark_aot(
            TARGET_NAME "${TARGET_NAME_ARK_AOT}"
            ABC_FILE "${GEN_ABC_FILE_NAME}"
            EXTRA_DEPS
                ${TARGET_NAME_COMPILE_ABC}
                ${ARG_EXTRA_DEPS}
            EXTRA_BOOT_PANDA_FILES ${ARG_EXTRA_BOOT_PANDA_FILES}
        )
        list(APPEND AOT_EXTRA_DEPS ${TARGET_NAME_ARK_AOT})

        set(AOT_FILES_OPTION ":${GEN_AOT_FILE_NAME}")
    endif()

    if (DEFINED ARG_MAKE_EXTRA_AOT_FILES)
        set(COUNTER 0)
        set(TARGET_NAME_ARK_AOT_EXTRA "${ARG_TARGET_NAME}_aot_extra_")
        foreach (EXTRA_AOT_ABC_FILE ${ARG_MAKE_EXTRA_AOT_FILES})
            math(EXPR COUNTER "${COUNTER} + 1")
            set(TARGET_NAME_ARK_AOT_I "${TARGET_NAME_ARK_AOT_EXTRA}_${COUNTER}")
            cmake_path(GET EXTRA_AOT_ABC_FILE STEM GEN_AOT_FILE_NAME)
            set(GEN_AOT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${GEN_AOT_FILE_NAME}.an")
            ark_aot(
                TARGET_NAME "${TARGET_NAME_ARK_AOT_I}"
                ABC_FILE "${EXTRA_AOT_ABC_FILE}"
                OUTPUT_FILE "${GEN_AOT_FILE_NAME}"
                EXTRA_DEPS
                    ${TARGET_NAME_COMPILE_ABC}
                    ${ARG_EXTRA_DEPS}
                EXTRA_BOOT_PANDA_FILES ${ARG_EXTRA_BOOT_PANDA_FILES}
            )
            list(APPEND AOT_EXTRA_DEPS ${TARGET_NAME_ARK_AOT_I})
            set(AOT_FILES_OPTION "${AOT_FILES_OPTION}:${GEN_AOT_FILE_NAME}")
        endforeach()
    endif()

    if (DEFINED AOT_FILES_OPTION)
        string(SUBSTRING "${AOT_FILES_OPTION}" 1 -1 AOT_FILES_OPTION)
        set(AOT_FILES_OPTION "--aot-files=${AOT_FILES_OPTION}")
        list(APPEND ARG_ADDITIONAL_OPTIONS "${AOT_FILES_OPTION}")
        list(APPEND ARG_EXTRA_DEPS ${AOT_EXTRA_DEPS})
    endif()


    run_panda_abc(
        TARGET_NAME "${ARG_TARGET_NAME}"
        ABC_FILE_NAME "${GEN_ABC_FILE_NAME}"
        ADDITIONAL_OPTIONS ${ARG_ADDITIONAL_OPTIONS}
        EXTRA_BOOT_PANDA_FILES ${ARG_EXTRA_BOOT_PANDA_FILES}
        EXTRA_DEPS
            "${ARG_EXTRA_DEPS}"
            ${TARGET_NAME_COMPILE_ABC}
    )

endfunction()

add_custom_target(stdlib_disasm
    COMMENT "Compiling: ${SRC_FULL_PATH}"
    COMMAND ${PANDA_DISASM} ${ETSSTDLIB_ABC} "etsstdlib.abc.pa"
    VERBATIM
)

add_subdirectory(panda_helpers)
add_subdirectory(lib)
add_subdirectory(app)
add_subdirectory(tests)
